//	Copyright (c) 2008, x-port.net ltd.
//
//	All rights reserved.
//
//	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
//
//	    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
//	    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
//	    * Neither the name of the x-port.net ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
//
//	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
//	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//   Purpose:  File system utility function definitions..
//   $Id$


#include <stdafx.h>
#include <Common/FileSystemUtil.h>

// Read a file from the local file system.
HRESULT xport::ReadFromFileSystem(const TCHAR *lpszPath, void **ppContent, DWORD *pdwDataSize)
{
	HRESULT hr = E_FAIL;
	// Check the arguments.
	if(lpszPath && ppContent) {
		// Open the file.
		HANDLE hFile = ::CreateFile(lpszPath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
		if(hFile != INVALID_HANDLE_VALUE) {
			// Create a file mapping.
			HANDLE hMap = ::CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
			if(hMap) {
				// Map the file.
				void *pFile = ::MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
				if(pFile) {
					DWORD dwSize = ::GetFileSize(hFile, 0);
					if(dwSize != INVALID_FILE_SIZE) {
						// Allocate memory for the file content.
						*ppContent = ::malloc(dwSize + 1);
						if(*ppContent) {
							// Copy the file contents.
							::memcpy(*ppContent, pFile, dwSize);
							*(reinterpret_cast<BYTE*>(*ppContent) + dwSize) = 0;
							// Return the file size if requested.
							if(pdwDataSize)
								*pdwDataSize = dwSize;
							hr = S_OK;
						} else
							hr = E_OUTOFMEMORY;
					}
					::UnmapViewOfFile(pFile);
				}
				::CloseHandle(hMap);
			}
			::CloseHandle(hFile);
		} else
			hr = E_POINTER;
	} else
		hr = E_INVALIDARG;
	return hr;
}

// Write data to the local file system [clobbers any existing file].
HRESULT xport::WriteToFileSystem(const TCHAR *lpszPath, const void *pContent, const DWORD dwDataSize)
{
	HRESULT hr = E_FAIL;
	// Check the arguments.
	if(lpszPath && pContent && dwDataSize > 0) {
		// Create the file [note that this will clobber any existing file].
		HANDLE hFile = ::CreateFile(lpszPath, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
		if(hFile != INVALID_HANDLE_VALUE) {
			// Create a file mapping.
			HANDLE hMap = ::CreateFileMapping(hFile, 0, PAGE_READWRITE, 0, dwDataSize, 0);
			if(hMap) {
				// Map the file.
				void *pFile = ::MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);
				if(pFile) {
					// Write the data to the memory-mapped file.
					::memcpy(pFile, pContent, dwDataSize);
					::UnmapViewOfFile(pFile);
					hr = S_OK;
				}
				::CloseHandle(hMap);
			}
			::CloseHandle(hFile);
		} else
			hr = E_ACCESSDENIED;
	} else
		hr = E_INVALIDARG;
	return hr;
}

// Write two separate data blocks to the local file system [clobbers any existing file].
HRESULT xport::WriteToFileSystem(const TCHAR *lpszPath, const void *pContent1, const DWORD dwDataSize1, const void *pContent2, const DWORD dwDataSize2)
{
	HRESULT hr = E_FAIL;
	// Check the arguments.
	if(lpszPath && pContent1 && dwDataSize1 > 0 && pContent2 && dwDataSize2 > 0) {
		// Create the file [note that this will clobber any existing file].
		HANDLE hFile = ::CreateFile(lpszPath, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
		if(hFile != INVALID_HANDLE_VALUE) {
			// Create a file mapping.
			HANDLE hMap = ::CreateFileMapping(hFile, 0, PAGE_READWRITE, 0, dwDataSize1 + dwDataSize2, 0);
			if(hMap) {
				// Map the file.
				void *pFile = ::MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);
				if(pFile) {
					// Write the data to the memory-mapped file.
					::memcpy(pFile, pContent1, dwDataSize1);
					::memcpy((char *)pFile + dwDataSize1, pContent2, dwDataSize2);
					::UnmapViewOfFile(pFile);
					hr = S_OK;
				}
				::CloseHandle(hMap);
			}
			::CloseHandle(hFile);
		} else
			hr = E_ACCESSDENIED;
	} else
		hr = E_INVALIDARG;
	return hr;
}

// Create a directory structure on the local file system, based on a target file name. Must be a full [i.e. not relative] path. Recursive.
HRESULT xport::CreateDirectoryStructure(const TCHAR *lpszPath)
{
	HRESULT hr = E_FAIL;
	// Check the argument.
	if(lpszPath) {
		// Hide the last token in the path.
#ifdef _UNICODE
		wchar_t *p = ::wcsrchr(lpszPath, L'\\');
#else
		char *p = ::strrchr(lpszPath, '\\');
#endif
		if(p) {
			*p = _T('\0');
			// Recurse.
			CreateDirectoryStructure(lpszPath);
			*p = _T('\\');
			// Try to open this directory.
			HANDLE hFile = ::CreateFile(lpszPath, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS , 0);
			// If we can't...
			if(hFile == INVALID_HANDLE_VALUE) {
				// ...then try to create it instead.
				if(::CreateDirectory(lpszPath, 0) == 0)
					hr = E_ACCESSDENIED;
				else
					hr = S_OK;
			} else {
				::CloseHandle(hFile);
				hr = S_FALSE;
			}
		} else
			hr = S_FALSE;
	} else
		hr = E_INVALIDARG;
	return hr;
}

// Return the home directory for the specified module handle.
HRESULT xport::GetHomeDirectory(HMODULE hModule, BSTR *psHomeDirectory) 
{
	HRESULT hr = S_OK;

	// Check the arguments.
	if(hModule && psHomeDirectory) {
		// Get the path of the specified module.
		TCHAR buf[MAX_PATH + 1];
		DWORD dw = ::GetModuleFileName(hModule, buf, MAX_PATH);
		buf[dw] = _T('\0');

		// We only want directory portion.
		TCHAR *p = _tcsrchr(buf, L'\\');
		if(p) {
			*p = _T('\0');
			CComBSTR s = buf;
			*psHomeDirectory = s.Detach();
		} else
			hr = E_ABORT;
	} else
		hr = E_INVALIDARG;

	return hr;
}

HRESULT xport::GetHomeDirectory(HMODULE hModule, CString &sHomeDirectory)
{
	HRESULT hr = S_OK;

	if(hModule)
	{
		TCHAR buf[MAX_PATH + 1];
		DWORD dw = ::GetModuleFileName(hModule, buf, MAX_PATH);
		buf[dw] = _T('\0');

		TCHAR *p = _tcsrchr(buf, L'\\');
		if(p)
		{
			*p = _T('\0');
			sHomeDirectory = buf;
		}
		else
			hr = E_ABORT;
	}
	else
		hr = E_INVALIDARG;

	return hr;
}
